/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.edu.agh.sao.engine;

import java.io.*;

/**
 * 
 * @author harry
 */
public class GameBoard {

	private Ball[] elements;
	private int size;

	public GameBoard(int size) {
		elements = new Ball[size * size];
		this.size = size;
	}

	public Ball get(Point p) {
		return elements[p.getX() * size + p.getY()];
	}

	public void fillRandom(int colors) {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				int color = (int) Math.ceil(colors * Math.random());
				elements[i * size + j] = new Ball(this, color);
			}
		}
	}

	/*public void testFill1() {
		assert (size == 2);
		elements[0] = new Ball(this, 0);
		elements[1] = new Ball(this, 0);
		elements[2] = new Ball(this, 1);
		elements[3] = new Ball(this, 2);
	}

	public void testFill2() {
		assert (size == 3);
		elements[0] = new Ball(this, 1);
		elements[1] = new Ball(this, 0);
		elements[2] = new Ball(this, 0);

		elements[3] = new Ball(this, 2);
		elements[4] = new Ball(this, 1);
		elements[5] = new Ball(this, 0);

		elements[6] = new Ball(this, 2);
		elements[7] = new Ball(this, 1);
		elements[8] = new Ball(this, 0);
	}*/

	public void fillFromFile(File file) throws Exception {
			FileReader fr = new FileReader(file);
			char[] input = new char[(int) file.length()];
			fr.read(input, 0, (int) file.length());
			fillFromString(new String(input));
	}
	
	public void fillFromString(String in) {
		String[] numbers = in.split(" ");
		int next = 0;
		for (int i = size - 1; i >= 0; i--) for (int j = 0; j < size; j++)
			elements[j * size + i] = new Ball(this, Integer.valueOf(numbers[next++].trim()));
	}
	
	public static GameBoard readBoardFromFile(String fileName) throws NumberFormatException, IOException{
		BufferedReader r = new BufferedReader(new FileReader(fileName));
		int boardSize = Integer.parseInt(r.readLine());
		GameBoard output = new GameBoard(boardSize);
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < boardSize; i++){
			sb.append(r.readLine().trim());
			if(i < boardSize - 1){
				sb.append(' ');
			}
		}
		output.fillFromString(sb.toString());
		return output;
	}

	public boolean click(int x, int y) {
		if (x < 0 || y < 0 || x >= size || y >= size
				|| elements[x * size + y] == null) {
			return false;
		}

		int color = elements[x * size + y].getColor();
		remove(x, y, color);
		makeItFall();
		return true;
	}

	private void remove(int x, int y, int color) {
		if (x < 0 || y < 0 || x >= size || y >= size
				|| elements[x * size + y] == null
				|| elements[x * size + y].getColor() != color) {
			return;
		}
		elements[x * size + y] = null;
		remove(x, y - 1, color);
		remove(x - 1, y, color);
		remove(x, y + 1, color);
		remove(x + 1, y, color);
	}

	public boolean click(Ball target) {
		int[] xy = find(target);
		if (xy != null && checkIfCanFall(xy)) {
			return click(xy[0], xy[1]);
		}
		return false;
	}

	private boolean checkIfCanFall(int[] xy) {
		int x = xy[0];
		int y = xy[1];
		int color = elements[x * size + y].getColor();

		if (x > 0) {
			Ball b = elements[(x - 1) * size + y];
			if (b != null && b.getColor() == color)
				return true;
		}
		if (x < size - 1) {
			Ball b = elements[(x + 1) * size + y];
			if (b != null && b.getColor() == color)
				return true;
		}
		if (y > 0) {
			Ball b = elements[x * size + y - 1];
			if (b != null && b.getColor() == color)
				return true;
		}
		if (y < size - 1) {
			Ball b = elements[x * size + y + 1];
			if (b != null && b.getColor() == color)
				return true;
		}
		return false;
	}

	public int[] find(Ball target) {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (elements[i * size + j] == target) {
					int[] result = new int[2];
					result[0] = i;
					result[1] = j;
					return result;
				}
			}
		}
		return null;
	}

	private void makeItFall() {
		for (int i = 0; i < size; i++) {
			makeColumnFall(i);
		}
	}

	private void makeColumnFall(int i) {
		for (int j = 0; j < size; j++) {
			if (elements[i * size + j] == null) {
				for (int k = j + 1; k < size; k++) {
					if (elements[i * size + k] != null) {
						elements[i * size + j] = elements[i * size + k];
						elements[i * size + k] = null;
						break;
					} else if (k == size - 1) {
						return;
					}
				}
			}
		}
	}

	@Override
	public String toString() {
		StringBuilder board = new StringBuilder();
		for (int i = size - 1; i >= 0; i--) {
			for (int j = 0; j < size; j++) {
				if (elements[j * size + i] != null)
					board.append(elements[j * size + i].getColor());
				else
					board.append('X');
				board.append(' ');
			}
			board.append('\n');
		}
		return board.toString();
	}

	public int countBalls() {
		int sum = 0;
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (elements[j * size + i] != null) {
					sum++;
				}
			}
		}
		return sum;
	}

	public GameBoard getGameCopy() {
		GameBoard tmp = new GameBoard(this.size);
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				int color = this.elements[i * size + j].getColor();
				tmp.elements[i * size + j] = new Ball(tmp, color);
			}
		}
		return tmp;
	}

	public int getSize() {
		return size;
	}
}
